﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using LotteryVote.Entity.Bet;
using LotteryVote.Data;
using LotteryVote.Entity;
using LotteryVote.Logic.Bet;
using LotteryVote.Utility;
using LotteryVote.Logic.Web;
using LotteryVote.Logic.Exceptions;
using System.Web;
using LotteryVote.Logic.Limit;

namespace LotteryVote.Logic
{
    public class BetFacade
    {
        #region Properties
        private UserInfoDataAccess _daUserInfo;
        internal UserInfoDataAccess DaUserInfo
        {
            get
            {
                if (_daUserInfo == null)
                    _daUserInfo = new UserInfoDataAccess();
                return _daUserInfo;
            }
            set
            {
                _daUserInfo = value;
            }
        }

        private BetSheetDataAccess _daBetSheet;
        internal BetSheetDataAccess DaBetSheet
        {
            get
            {
                if (_daBetSheet == null)
                    _daBetSheet = new BetSheetDataAccess();
                return _daBetSheet;
            }
            set
            {
                _daBetSheet = value;
            }
        }

        private BetOrderDataAccess _daBetOrder;
        internal BetOrderDataAccess DaBetOrder
        {
            get
            {
                if (_daBetOrder == null)
                    _daBetOrder = new BetOrderDataAccess();
                return _daBetOrder;
            }
            set
            {
                _daBetOrder = value;
            }
        }

        private OrderAncestorCommInfoDataAccess _daAncestorComm;
        internal OrderAncestorCommInfoDataAccess DaAncestorComm
        {
            get
            {
                if (_daAncestorComm == null)
                    _daAncestorComm = new OrderAncestorCommInfoDataAccess();
                return _daAncestorComm;
            }
            set
            {
                _daAncestorComm = value;
            }
        }

        private BetResultDataAccess _daBetResult;
        internal BetResultDataAccess DaBetResult
        {
            get
            {
                if (_daBetResult == null)
                    _daBetResult = new BetResultDataAccess();
                return _daBetResult;
            }
            set
            {
                _daBetResult = value;
            }
        }

        private LimitChecker _checker;
        internal LimitChecker Checker
        {
            get
            {
                if (_checker == null)
                    _checker = new LimitChecker();
                return _checker;
            }
            set
            {
                _checker = value;
            }
        }

        private FreezeFundsFacade _freezeFundFacade;
        public FreezeFundsFacade FreezeFundsFacade
        {
            get
            {
                if (_freezeFundFacade == null)
                    _freezeFundFacade = new FreezeFundsFacade();
                return _freezeFundFacade;
            }
            set
            {
                _freezeFundFacade = value;
            }
        }

        private BetOrderBuilder _orderBuilder;
        internal BetOrderBuilder OrderBuilder
        {
            get
            {
                if (_orderBuilder == null)
                    _orderBuilder = new BetOrderBuilder();
                return _orderBuilder;
            }
            set
            {
                _orderBuilder = value;
            }
        }

        #endregion

        /// <summary>
        /// 下注单.
        /// </summary>
        /// <param name="user">The user.</param>
        /// <param name="specieId">The specie id.</param>
        /// <param name="betList">The bet list.</param>
        public string AddBet(User user, LotterySpeciesEnum specie, IList<BetItem> betList)
        {
            var betSheetDic = OrderBuilder.BuildOrder(user, specie, betList);
            AddBetSheet(user, betSheetDic, OrderBuilder.ActualTurnover);
            return string.Format("Bet {0},Accept {1}", OrderBuilder.ExceptTurnover, OrderBuilder.ActualTurnover);
        }
        public string AddBet(User user, LotterySpeciesEnum specie, IList<AutoBetItem> betList)
        {
            decimal exceptTurnover = 0;
            decimal actualTurnver = 0;
            if (betList != null && betList.Count > 0)
            {
                IDictionary<BetSheet, IList<BetOrder>> betSheetDic = new Dictionary<BetSheet, IList<BetOrder>>();
                foreach (var betOrder in betList)
                {
                    var sheet = OrderBuilder.BuildAutoBetOrder(user, specie, betOrder.CompanyList, betOrder.WagerList, betOrder.BetType);
                    betSheetDic.AddRange(sheet);
                    exceptTurnover += OrderBuilder.ExceptTurnover;
                    actualTurnver += OrderBuilder.ActualTurnover;
                }


                List<BetOrder> orderList = new List<BetOrder>();
                foreach (var item in betSheetDic)
                    orderList.AddRange(item.Value);
                BetSheet orderSheet = new BetSheet
                {
                    Num = betList.First().BetType.ToString(),
                    Turnover = orderList.Sum(it => it.Turnover),
                    NetAmount = orderList.Sum(it => it.NetAmount),
                    Commission = orderList.Sum(it => it.Commission),
                    UserId = user.UserId,
                    Status = BetStatus.Valid,
                    IPAddress = IPHelper.IPAddress
                };
                betSheetDic.Clear();
                betSheetDic.Add(orderSheet, orderList);
                AddBetSheet(user, betSheetDic, actualTurnver);
            }
            return string.Format("Bet {0},Accept {1}", exceptTurnover, actualTurnver);
        }
        /// <summary>
        ///取消注单.
        /// </summary>
        /// <param name="sheetId">The sheet id.</param>
        /// <param name="userId">The user id.</param>
        public void CancelBetSheet(int sheetId, User user)
        {
            var sheet = DaBetSheet.GetBetSheet(sheetId);
            if (sheet.UserId != user.UserId && user.Role != SystemRole.Company)
                throw new NoPermissionException("Cancel betSheet", string.Format("User {0} can't cancel betsheet {1}", user.UserId, sheetId));
            var orderList = DaBetOrder.ListOrderBySheet(sheetId);
            orderList.ForEach(it =>
            {
                Checker.RollLimit(it);
            });
            DaBetSheet.ExecuteWithTransaction(() =>
            {
                DaBetOrder.Tandem(DaBetSheet);
                FreezeFundsFacade.DaFreezeFunds.Tandem(DaBetSheet);     //串联事务
                orderList.ForEach(it => CancelBetOrder(it, sheet.UserId));
                DaBetSheet.UpdateBetSheetStatus(sheetId, BetStatus.Invalid);
                Checker.UpdateLimit(DaBetSheet);
            });

        }
        public void CancelBetOrder(int orderId, User user)
        {
            var order = DaBetOrder.GetBetOrder(orderId);
            if (user.Role != SystemRole.Company && user.UserId != order.UserId)
                throw new NoPermissionException("Cancel betOrder", string.Format("User {0} can't cancel betOrder {1}", user.UserId, orderId));
            Checker.RollLimit(order);
            DaBetOrder.ExecuteWithTransaction(() =>
            {
                FreezeFundsFacade.DaFreezeFunds.Tandem(DaBetOrder); //串联事物
                CancelBetOrder(order, order.UserId);
                Checker.UpdateLimit(DaBetOrder);
            });
        }
        /// <summary>
        /// 取消子注单.
        /// </summary>
        /// <param name="orderId">The order id.</param>
        /// <param name="userId">The user id.</param>
        public void CancelBetOrder(int orderId, int userId)
        {
            var order = DaBetOrder.GetBetOrder(orderId);
            if (order.UserId != userId)
                throw new NoPermissionException("Cancel betOrder", string.Format("User {0} can't cancel betOrder {1}", userId, orderId));
            Checker.RollLimit(order);
            DaBetOrder.ExecuteWithTransaction(() =>
            {
                FreezeFundsFacade.DaFreezeFunds.Tandem(DaBetOrder); //串联事物
                CancelBetOrder(order, userId);
                Checker.UpdateLimit(DaBetOrder);
            });

        }
        private void CancelBetOrder(BetOrder order, int userId)
        {
            if (order.Status != BetStatus.Valid) return;
            DaBetOrder.UpdateState(order.OrderId, BetStatus.Invalid);
            FreezeFundsFacade.UnFreezeUserFunds(userId, order.Turnover);    //解冻资金
        }
        /// <summary>
        /// Gets the bet sheets.
        /// </summary>
        /// <param name="userId">The user id.</param>
        /// <returns></returns>
        public IList<BetSheet> GetBetSheets(int userId)
        {
            return DaBetSheet.ListBetSheet(userId);
        }
        public IList<BetSheet> GetTodayBetSheets(int userId, int pageCount, int pageIndex, out int totalCount)
        {
            totalCount = DaBetSheet.CountBetSheet(userId, DateTime.Today);
            return DaBetSheet.ListBetSheet(userId, DateTime.Today, pageCount, pageIndex);
        }
        public IList<BetSheet> GetTodayValidBetSheets(int pageCount, int pageIndex, out int totalCount)
        {
            totalCount = DaBetSheet.CountBetSheet(BetStatus.Valid, DateTime.Today);
            return DaBetSheet.ListBetSheet(BetStatus.Valid, DateTime.Today, pageCount, pageIndex);
        }
        public IList<BetOrder> GetBetOrderBySheet(int sheetId, int pageItemCount, int pageIndex, out int totalCount)
        {
            totalCount = DaBetOrder.CountOrder(sheetId);
            return DaBetOrder.ListOrderBySheet(sheetId, pageItemCount, pageIndex);
        }
        public IList<BetOrder> GetBetOrderBySheet(int sheetId, string num, int companyId)
        {
            throw new NotImplementedException();
        }
        public IList<BetOrder> GetTodayValidOrder(int pageCount, int pageIndex, out int totalCount)
        {
            totalCount = DaBetOrder.CountOrder(BetStatus.Valid, DateTime.Today);
            return DaBetOrder.ListOrder(BetStatus.Valid, DateTime.Today, pageCount, pageIndex);
        }
        public IList<BetOrder> FindTodayOrder(string num, int companyId, int gameplaywayId, string orderField, string sortType, int pageItemCount, int pageIndex, out int totalCount)
        {
            totalCount = DaBetOrder.CountOrder(BetStatus.Valid, DateTime.Today, num, companyId, gameplaywayId);
            return DaBetOrder.ListOrderByCondition(BetStatus.Valid, DateTime.Today, num, companyId, gameplaywayId, orderField, sortType, pageItemCount, pageIndex);
        }
        /// <summary>
        /// 获取指定用户今日未结算注单.
        /// </summary>
        /// <param name="user">是否加载父级佣金信息.</param>
        /// <param name="loadAncestorComm">if set to <c>true</c> [load ancestor comm].</param>
        /// <returns></returns>
        public IList<BetOrder> GetTodayUnSettleOrderByUser(int userId, bool loadAncestorComm = false)
        {
            var orderList = DaBetOrder.ListOrder(userId, BetStatus.Valid, DateTime.Today);
            if (loadAncestorComm)
                orderList.ForEach(it =>
                {
                    it.AncestorCommission = DaAncestorComm.GetAncestorComms(it);
                });
            return orderList;
        }
        /// <summary>
        /// 获取今日未结（有效）的注单.
        /// </summary>
        /// <returns></returns>
        public IList<BetOrder> GetTodayUnSettleOrder()
        {
            return DaBetOrder.ListOrder(BetStatus.Valid, DateTime.Today);
        }
        public IList<BetOrder> GetTodayUnSettleOrder(int companyId)
        {
            return DaBetOrder.ListOrder(BetStatus.Valid, companyId, DateTime.Today);
        }
        public IList<BetOrder> GetTodaySettleOrderByUser(int userId, int CompanyId, bool loadAncestorComm = false)
        {
            var orderList = DaBetOrder.ListOrder(userId, CompanyId, BetStatus.Settled, DateTime.Today);
            if (loadAncestorComm)
                orderList.ForEach(it =>
                {
                    it.AncestorCommission = DaAncestorComm.GetAncestorComms(it);
                });
            return orderList;
        }
        /// <summary>
        /// 获取用户的子孙注单.
        /// </summary>
        /// <param name="userId">The user id.</param>
        /// <param name="date">The date.</param>
        /// <returns></returns>
        public IList<BetOrder> GetDescendantSettledOrderByUser(int userId, DateTime date)
        {
            return DaBetOrder.ListDescendantOrder(userId, BetStatus.Settled, date);
        }
        public IList<BetOrder> GetDescendantSettledOrderByUser(int userId, DateTime fromDate, DateTime toDate)
        {
            return DaBetOrder.ListDescendantOrder(userId, BetStatus.Settled, fromDate, toDate);
        }
        public IList<BetOrder> GetSettledOrderByUser(int userId, DateTime fromDate, DateTime toDate)
        {
            return DaBetOrder.ListOrder(userId, BetStatus.Settled, fromDate, toDate);
        }
        public IList<BetOrder> GetTodayOrderByUser(int userId, int pageItemCount, int pageIndex, out int totalCount)
        {
            totalCount = DaBetOrder.CountOrder(userId, DateTime.Today);
            return DaBetOrder.ListOrder(userId, DateTime.Today, pageItemCount, pageIndex);
        }
        /// <summary>
        /// 添加注单.
        /// </summary>
        /// <param name="user">The user.</param>
        /// <param name="betSheetDic">The bet sheet dic.</param>
        /// <param name="totalTurnover">The total turnover.</param>
        private void AddBetSheet(User user, IDictionary<BetSheet, IList<BetOrder>> betSheetDic, decimal totalTurnover)
        {
            CheckGivenCredit(user.UserId, totalTurnover);
            DaBetSheet.ExecuteWithTransaction(() =>
            {
                FreezeFundsFacade.DaFreezeFunds.Tandem(DaBetSheet);
                FreezeFundsFacade.FreezeUserFunds(user.UserId, totalTurnover);   //冻结用户资金
                InsertBetOrderToDataBase(betSheetDic);
                Checker.UpdateLimit(DaBetSheet);
            });
        }
        /// <summary>
        /// 结算今日注单.
        /// </summary>
        internal void SettleBetOrder(int companyId)
        {
            var orderList = GetTodayUnSettleOrder(companyId);
            foreach (var order in orderList)
            {
                int winmultiple = WinMultipleCalculator.GetCalculator().Calculate(order);   //获取输赢倍数
                decimal winResult;
                if (winmultiple > 0)
                    winResult = (order.Amount * (decimal)(order.Odds * winmultiple)) - order.NetAmount; //若倍数大于0，则为赢，赢钱=下注额×赔率×倍数-净额
                else
                    winResult = order.NetAmount * -1;       //若倍数小于等于0，则为输，输钱=净额×-1
                order.DrawResult = winResult;
                order.Status = BetStatus.Settled;
            }
            DaBetOrder.ExecuteWithTransaction(() =>
            {
                orderList.ForEach(it =>
                {
                    DaBetOrder.Update(it);
                    FreezeFundsFacade.UnFreezeOrder(it);
                    //DaBetOrder.SetDrawResult(it);
                });
            });
        }

        private void CheckGivenCredit(int userId, decimal turnover)
        {
            var userInfo = DaUserInfo.GetUserInfo(userId);
            if (userInfo.AvailableGivenCredit < turnover)
                throw new InvalidGivenCreditException(userInfo.AvailableGivenCredit, turnover);
        }
        /// <summary>
        /// 将注单写入数据库.
        /// </summary>
        /// <param name="betSheetDic">The bet sheet dic.</param>
        private void InsertBetOrderToDataBase(IDictionary<BetSheet, IList<BetOrder>> betSheetDic)
        {
            string ipAddress = IPHelper.IPAddress;
            DaBetSheet.ExecuteWithTransaction(() =>
            {
                DaAncestorComm.Tandem(DaBetSheet);
                foreach (var betSheet in betSheetDic)
                {
                    var sheet = betSheet.Key;
                    sheet.IPAddress = ipAddress;
                    DaBetSheet.InsertBetSheet(sheet);
                    DaBetOrder.Tandem(DaBetSheet);
                    foreach (var order in betSheet.Value)
                    {
                        order.SheetId = sheet.SheetId;
                        DaBetOrder.Insert(order);
                        order.AncestorCommission.ForEach(it =>
                        {
                            it.OrderId = order.OrderId;
                            DaAncestorComm.Insert(it);
                        });
                    }
                }
            });
        }
    }
}
